註: 筆者覺得這章節用的 scull (Simple Character Utility for Loading Localities) 有點過時了,上網找到類似,但更喜歡的範例分享
在列出 /dev/ 目錄時,你會看到每個設備文件的條目中有兩個用逗號分隔的數字(通常在文件最後修改日期前面顯示),這兩個數字就是設備的 主設備號(major number) 和 次設備號(minor number)
brw-rw----. 1 root disk 259, 0 Sep 6 13:10 nvme0n1
brw-rw----. 1 root disk 259, 1 Sep 6 13:10 nvme0n1p1
lrwxrwxrwx. 1 root root 15 Sep 6 13:10 stderr -> /proc/self/fd/2
lrwxrwxrwx. 1 root root 15 Sep 6 13:10 stdin -> /proc/self/fd/0
lrwxrwxrwx. 1 root root 15 Sep 6 13:10 stdout -> /proc/self/fd/1
crw-rw-rw-. 1 root tty 5, 0 Sep 6 13:10 tty
crw--w----. 1 root tty 4, 0 Sep 6 13:10 tty0
crw--w----. 1 root tty 4, 1 Sep 6 13:10 tty1
在撰寫 Linux 字符設備驅動程式時,設備號碼的分配與釋放是第一步需要處理的工作。Linux 為驅動程式提供了兩種分配設備號的方式:靜態分配和動態分配
int register_chrdev_region(dev_t first, unsigned int count, char *name);
參數說明:
first: 你想分配的第一個設備號(包括主設備號和次設備號),通常次設備號設為 0。
count: 你希望分配的設備號總數。
name: 設備名稱,該名稱將出現在 /proc/devices 和 sysfs 中。
回傳值:
成功時返回 0,如果失敗,返回負數錯誤碼。
範例:
dev_t dev;
int result = register_chrdev_region(MKDEV(240, 0), 1, "mychardev");
if (result < 0) {
printk(KERN_WARNING "Can't allocate device number\n");
return result;
}
int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);
參數說明:
dev: 指向 dev_t 的指標,當分配成功後,內核將分配的主設備號和次設備號儲存在此處
firstminor: 要求分配的第一個次設備號,通常設為 0
count: 要分配的設備數量
name: 設備名稱,將顯示在 /proc/devices 和 sysfs 中
回傳值:
成功時返回 0,如果失敗,返回負數錯誤碼
範例:
dev_t dev;
int result = alloc_chrdev_region(&dev, 0, 1, "mychardev");
if (result < 0) {
printk(KERN_WARNING "Can't allocate device number\n");
return result;
}
printk(KERN_INFO "Allocated device number: major=%d, minor=%d\n", MAJOR(dev), MINOR(dev));
在這個範例中,內核會動態分配一個主設備號,次設備號設為 0,並分配 1 個設備號
void unregister_chrdev_region(dev_t first, unsigned int count);
參數說明:
first: 要釋放的起始設備號
count: 要釋放的設備號的數量
範例:
unregister_chrdev_region(dev, 1);
在這個範例中,釋放了一個先前分配的設備號。
We can consider the file to be an “object” and the functions operating on it to be its “methods,” using object-oriented programming terminology to denote actions declared by an object to act on itself
The file_operations structure is how a char driver sets up this connection.
file_operations 結構體的定義
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*aio_read) (struct kiocb *, char __user *, size_t, loff_t);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*aio_write) (struct kiocb *, const char __user *, size_t, loff_t);
int (*readdir) (struct file *, void *, filldir_t);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, struct dentry *, int datasync);
int (*aio_fsync) (struct kiocb *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void __user *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area) (struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
};
NOTE: 未顯式賦值的結構成員將默認為 NULL
file_operations 結構體的實例通常命名為 fops,這是 Linux 字符設備驅動中的慣例
struct file_operations fops = {
.read = device_read,
.write = device_write,
.open = device_open,
.release = device_release
};
As you read through the list of file_operations methods, you will note that a number of parameters include the string "__user"
<linux/fs.h> 定義了文件操作和文件系統交互的核心結構,如 file_operations、inode、file 等。這些結構通過函數指標將具體的設備操作與系統調用連接起來: